-
Notifications
You must be signed in to change notification settings - Fork 903
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(core/entityTag): Refactor Entity Tags to React #3717
refactor(core/entityTag): Refactor Entity Tags to React #3717
Conversation
} | ||
|
||
@autoBindMethods | ||
class EntityTagMessage extends React.Component<IEntityTagMessageProps, {}> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I split these components out, but left them in the same file. Good? Bad? Separate files? The option component is teeny and I didn't think deserved a file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine by me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small enough, only used here....
ReactInjector, TaskMonitor, TaskMonitorBuilder | ||
} from 'core'; | ||
|
||
import { BasicFieldLayout, TextArea, ReactModal } from 'core/presentation'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these imports were imported from 'core'
before, and ended up being used while they were still undefined
because of timing and dependency loops
entityRef: IEntityRef; | ||
isNew: boolean; | ||
show?: boolean; | ||
onHide?(event: any): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do yall like or hate this syntax?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
works for me! doesn't look like there are arguments though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The caller can access the trigger event from the click event handler or whatever. IDK if it's necessary.
I used onHide.apply
to propagate any arguments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure!
const { isNew, tag, ownerOptions } = this.props; | ||
const message = this.state.message || ''; | ||
|
||
const closeButton = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may deserve its own component in its own file.
React modal has a closeButton={true}
support but it uses stock bootstrap markup, which doesn't look the same. In the future, a PR to react-bootstrap
allows a component to be passed like closeButton={DeckCloseButton}
</div> | ||
); | ||
|
||
const { TaskMonitorWrapper } = NgReact; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dereference NgReact components inside render to avoid trouble
export interface IState extends IFormComponentState { } | ||
|
||
/** A simple Formsy form component for validated <input> tags (text or checkbox) */ | ||
export class Input extends FormComponent<string, IProps, IState> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not actually using this component, but this shows a simpler approach which doesn't accept a layout strategy.
*/ | ||
@autoBindMethods() | ||
export class TextArea extends FormComponent<string, IProps, IState> { | ||
public static contextTypes = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This grants the TextArea component access to the formsy
context object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type your context, then contextTypes has type React.ValidationMap<ITextAreaContext>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
your context
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const isInvalid = this.showError() || this.showRequired(); | ||
const isDirty = !this.isPristine(); | ||
const inputClass = `form-control ${className} ${isInvalid ? 'ng-invalid' : ''} ${isDirty ? 'ng-dirty' : ''}`; | ||
const _input = <textarea className={inputClass} rows={rows} name={name} onChange={this.changeValue} value={this.getValue()} />; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This builds the actual <textarea>
element and wires the data and events
const _error = errorMessage && isDirty && <span className="error-message">{errorMessage}</span>; | ||
|
||
const FormFieldLayout = layout; | ||
return ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This delegates to the layout, passing it the 4 elements: label
, input
, help
, error
// Type wrapper for Formsy-React | ||
// Project: https://github.com/christianalfoni/formsy-react | ||
|
||
declare module 'formsy-react' { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are no @types/formsy-react
, so this is a very incomplete typings file for it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some generally minor stuff but LGTM overall - very nice work.
private $uibModalInstanceEmulation: IModalServiceInstance & { deferred?: IDeferred<any> }; | ||
|
||
/** Shows the Entity Tag Editor modal */ | ||
public static show(props: IEntityTagEditorProps): Promise<any> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Promise<void>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the generic sense, it (Modal.show
) actually does resolve to the "onHide action". This could be used to return a promise for a radio choice in a modal, for example.
In this case (EntityTagEditor) it doesn't resolve to anything, so changing to void is proper
isValid: false, | ||
isSubmitting: false, | ||
owner: owner, | ||
entityType: this.props.entityType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move to destructuring above
application: application, | ||
title: `${isNew ? 'Create' : 'Update'} ${this.props.tag.value.type} for ${entityRef.entityId}`, | ||
modalInstance: this.$uibModalInstanceEmulation, | ||
onTaskComplete: () => this.props.onUpdate(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
destructure?
<button type="submit" className="btn btn-primary" disabled={!this.state.isValid || this.state.isSubmitting}> | ||
<span className="glyphicon glyphicon-ok-circle" /> | ||
<span>{this.props.isNew ? ' Create' : ' Update'} {this.props.tag.value.type}</span> | ||
</button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth replacing with SubmitButton
- might not be, what do I know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting a submit button that's reused elsewhere (code in its own file), or simply extracting it to a const
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there already exists a <SubmitButton>
React component in there somewhere...you just have to find it.
|
||
@autoBindMethods | ||
class EntityTagMessage extends React.Component<IEntityTagMessageProps, {}> { | ||
private handleTextareaChanged(event: React.FormEvent<any>): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any
can probably be HTMLTextAreaElement
public constructor(private $timeout: ng.ITimeoutService, private $uibModal: IModalService, | ||
private confirmationModalService: any, private entityTagWriter: EntityTagWriter) { | ||
public constructor(private $timeout: ng.ITimeoutService, | ||
private confirmationModalService: any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be typed to ConfirmationModalService
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also import ITimeoutService
from angular
}; | ||
|
||
private helpContentsRegistry: HelpContentsRegistry; | ||
private helpContents: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be typed to something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ [key: string]: string }
probably
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or the IHelpContents
type from below
} | ||
} | ||
|
||
public render(): React.ReactElement<any> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove return type
* | | || | ||
* | +---------------------------------------+| | ||
* +----------------------------------------------------------+ | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice ascii art!
@@ -1,3 +1,5 @@ | |||
export * from './HoverablePopover'; | |||
export * from './Tooltip'; | |||
export * from './LabelComponent'; | |||
export * from './ModalService'; | |||
export * from './formsy/index'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer export * from './formsy
since there's an index
in there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a few questions and style things, but LGTM. great work!
entityRef: IEntityRef; | ||
isNew: boolean; | ||
show?: boolean; | ||
onHide?(event: any): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure!
</div> | ||
); | ||
|
||
const submitButton = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might make sense to use SubmitButton and add support for custom button text to support the {tag.value.type}
bit.
return ( | ||
<Modal show={this.state.show} onHide={this.onHide} dialogClassName="entity-tag-editor-modal"> | ||
|
||
<TaskMonitorWrapper monitor={this.state.taskMonitor} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right?!
} | ||
|
||
@autoBindMethods | ||
class EntityTagMessage extends React.Component<IEntityTagMessageProps, {}> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small enough, only used here....
<TextArea | ||
label="Message" | ||
_help={<div>Markdown is okay <HelpField id="markdown.examples"/></div>} | ||
layout={BasicFieldLayout} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
import { FormComponent, IFormComponentState, IFormComponentProps } from '../FormComponent'; | ||
|
||
export interface IProps extends IFormComponentProps { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where did we land on exported props names? I like the pattern of if its exported to name it...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is exported because Typescript requires it, but it is not re-exported from the module index:
export { Input } from './Input';
export { TextArea } from './TextArea';
cool?
|
||
public static defaultProps = { | ||
name: null as any, | ||
onChange: () => null as any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whats the benefit of null as any
over {}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no benefit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've imported noop
from core/utils
in other projects which I think worked nicely. Then you don't have to keep creating the same () => {}
everywhere
*/ | ||
@autoBindMethods() | ||
export class TextArea extends FormComponent<string, IProps, IState> { | ||
public static contextTypes = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type your context, then contextTypes has type React.ValidationMap<ITextAreaContext>
formsy: PropTypes.any | ||
}; | ||
|
||
public static defaultProps = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default props should have a type of Partial<IProps>
|
||
/** An interface which Formsy form field layouts should accept as props */ | ||
export interface IFormFieldLayoutProps extends HTMLAttributes<any> { | ||
_label?: React.ReactElement<any>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so many underscores...
6e0e1b2
to
3f93109
Compare
- Introduce imperative React Modal API - Introduce Formsy validation for React
3f93109
to
cb5f853
Compare
- Introduce imperative React Modal API - Introduce Formsy validation for React - chore(travis): run yarn
I guess I should fix the tests too